"""
用户认证路由模块
实现用户注册、登录、权限管理等认证相关接口
"""

# 导入FastAPI相关组件
from fastapi import APIRouter, Depends, HTTPException, status
# 导入Pydantic模型用于数据验证
from pydantic import BaseModel
# 导入类型提示
from typing import List, Optional
# 导入日期时间处理模块
from datetime import datetime, timedelta
# 导入JWT相关模块
import jwt
# 导入哈希库用于密码加密
from passlib.context import CryptContext
# 导入操作系统相关模块
import os
# 导入系统模块
import sys
# 导入SQLAlchemy会话
from sqlalchemy.orm import Session

# 将项目根目录添加到Python路径中
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

# 导入模型和数据库会话
from utils.models import User, get_db, Role, Permission, RolePermission
# 导入配置
from utils.config import Config

# 创建API路由实例
router = APIRouter(
    prefix="/api/v1/auth",  # 路由前缀
    tags=["auth"],  # 标签
    responses={404: {"description": "Not found"}},  # 响应定义
)

# 配置密码哈希处理上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# JWT配置
SECRET_KEY = Config.SECRET_KEY  # 实际项目中应从环境变量读取
ALGORITHM = "HS256"  # JWT算法
ACCESS_TOKEN_EXPIRE_MINUTES = 30  # 访问令牌过期时间（分钟）

# 数据模型定义
class UserLogin(BaseModel):
    """
    用户登录数据模型
    """
    username: str  # 用户名
    password: str  # 密码

class UserRegister(BaseModel):
    """
    用户注册数据模型
    """
    username: str  # 用户名
    email: str     # 邮箱
    password: str  # 密码

class Token(BaseModel):
    """
    JWT令牌数据模型
    """
    access_token: str  # 访问令牌
    token_type: str    # 令牌类型

class TokenData(BaseModel):
    """
    JWT令牌数据模型
    """
    username: Optional[str] = None  # 用户名

class UserResponse(BaseModel):
    """
    用户响应数据模型
    """
    id: int        # 用户ID
    username: str  # 用户名
    email: str     # 邮箱
    role: str      # 角色

    class Config:
        from_attributes = True  # 启用ORM模式

class UserRoleUpdate(BaseModel):
    """
    用户角色更新数据模型
    """
    role: str  # 角色

class RoleCreate(BaseModel):
    """
    角色创建数据模型
    """
    name: str                    # 角色名称
    description: Optional[str] = None  # 角色描述

class RoleResponse(BaseModel):
    """
    角色响应数据模型
    """
    id: int                      # 角色ID
    name: str                    # 角色名称
    description: Optional[str] = None  # 角色描述

    class Config:
        from_attributes = True  # 启用ORM模式

class PermissionCreate(BaseModel):
    """
    权限创建数据模型
    """
    name: str                    # 权限名称
    description: Optional[str] = None  # 角色描述

class PermissionResponse(BaseModel):
    """
    权限响应数据模型
    """
    id: int                      # 权限ID
    name: str                    # 权限名称
    description: Optional[str] = None  # 角色描述

    class Config:
        from_attributes = True  # 启用ORM模式

class RolePermissionAssign(BaseModel):
    """
    角色权限分配数据模型
    """
    role_id: int       # 角色ID
    permission_id: int # 权限ID

# 导入OAuth2密码Bearer认证方案
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login")

# 依赖项定义
def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)):
    """
    获取当前用户依赖项
    通过JWT令牌验证用户身份
    Args:
        token (str): JWT访问令牌
        db (Session): 数据库会话
    Returns:
        User: 当前用户对象
    Raises:
        HTTPException: 当令牌无效或用户不存在时抛出异常
    """
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="无法验证凭据",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        # 解码JWT令牌
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        token_data = TokenData(username=username)
    except jwt.JWTError:
        raise credentials_exception
    
    # 查询用户
    user = db.query(User).filter(User.username == username).first()
    if user is None:
        raise credentials_exception
    return user

def get_current_active_user(current_user: User = Depends(get_current_user)):
    """
    获取当前活跃用户依赖项
    检查用户是否处于激活状态
    Args:
        current_user (User): 当前用户对象
    Returns:
        User: 当前活跃用户对象
    Raises:
        HTTPException: 当用户被禁用时抛出异常
    """
    if current_user.is_active == 0:
        raise HTTPException(status_code=400, detail="用户已被禁用")
    return current_user

def get_current_active_admin(current_user: User = Depends(get_current_active_user)):
    """
    获取当前管理员用户依赖项
    检查用户是否具有管理员权限
    Args:
        current_user (User): 当前用户对象
    Returns:
        User: 当前管理员用户对象
    Raises:
        HTTPException: 当用户不是管理员时抛出异常
    """
    if current_user.role != "admin":
        raise HTTPException(status_code=403, detail="需要管理员权限")
    return current_user

# 路由定义
@router.post("/register", response_model=Token)
async def register_user(user: UserRegister, db: Session = Depends(get_db)):
    """
    用户注册接口
    创建新用户并生成JWT访问令牌
    Args:
        user (UserRegister): 用户注册信息
        db (Session): 数据库会话
    Returns:
        Token: JWT访问令牌
    Raises:
        HTTPException: 当用户名或邮箱已存在时抛出异常
    """
    try:
        # 检查用户是否已存在
        db_user = db.query(User).filter(User.username == user.username).first()
        if db_user:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="用户名已存在",
            )
        
        # 检查邮箱是否已存在
        db_user = db.query(User).filter(User.email == user.email).first()
        if db_user:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="邮箱已被使用",
            )
        
        # 创建用户
        hashed_password = get_password_hash(user.password)
        db_user = User(
            username=user.username,
            email=user.email,
            hashed_password=hashed_password,
            role="researcher", # 默认是研究员
        )
        db.add(db_user)
        db.commit()
        db.refresh(db_user)
        
        # 生成访问令牌
        access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
        access_token = create_access_token(
            data={"sub": user.username}, 
            expires_delta=access_token_expires
        )
        
        return {"access_token": access_token, "token_type": "bearer"}
    except HTTPException as e:
        raise e
    except Exception as e:
        print(e)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="注册失败，请稍后重试",
        )

@router.post("/login", response_model=Token)
async def login_user(user: UserLogin, db: Session = Depends(get_db)):
    """
    用户登录接口
    验证用户凭据并生成JWT访问令牌
    Args:
        user (UserLogin): 用户登录信息
        db (Session): 数据库会话
    Returns:
        Token: JWT访问令牌
    Raises:
        HTTPException: 当用户名或密码不正确时抛出异常
    """
    try:
        # 获取用户
        db_user = db.query(User).filter(User.username == user.username).first()
        
        # 验证用户是否存在
        if not db_user:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="用户名或密码不正确",
                headers={"WWW-Authenticate": "Bearer"},
            )
        
        # 验证用户是否处于激活状态
        if db_user.is_active == 0:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="用户账户已被禁用",
                headers={"WWW-Authenticate": "Bearer"},
            )
        
        # 验证密码
        if not verify_password(user.password, db_user.hashed_password):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="用户名或密码不正确",
                headers={"WWW-Authenticate": "Bearer"},
            )
        
        # 生成访问令牌
        access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
        access_token = create_access_token(
            data={"sub": db_user.username}, 
            expires_delta=access_token_expires
        )
        
        # 返回符合前端期望的格式
        return {"access_token": access_token, "token_type": "bearer", "token": access_token}
    except HTTPException as e:
        raise e
    except Exception as e:
        print(e)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="登录失败，请稍后重试",
        )

@router.post("/logout")
async def logout_user():
    """
    用户登出接口
    JWT是无状态的，实际项目中可以将token加入黑名单
    Returns:
        dict: 登出成功信息
    """
    return {"message": "用户已登出"}

@router.get("/me", response_model=UserResponse)
async def read_users_me(current_user: User = Depends(get_current_active_user)):
    """
    获取当前用户信息接口
    返回当前认证用户的详细信息
    Args:
        current_user (User): 当前用户对象
    Returns:
        UserResponse: 当前用户信息
    """
    return current_user

@router.put("/{user_id}/role", response_model=UserResponse)
async def update_user_role(
    user_id: int,
    role_update: UserRoleUpdate,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_active_admin)
):
    """
    更新用户角色接口（仅管理员）
    为指定用户分配新的角色
    Args:
        user_id (int): 用户ID
        role_update (UserRoleUpdate): 角色更新信息
        db (Session): 数据库会话
        current_user (User): 当前管理员用户
    Returns:
        UserResponse: 更新后的用户信息
    Raises:
        HTTPException: 当用户或角色不存在时抛出异常
    """
    try:
        # 检查用户是否存在
        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            raise HTTPException(status_code=404, detail="用户未找到")
        
        # 检查角色是否存在
        role = db.query(Role).filter(Role.name == role_update.role).first()
        if not role:
            raise HTTPException(status_code=400, detail="角色不存在")
        
        # 更新用户角色
        user.role = role_update.role
        db.commit()
        db.refresh(user)
        
        return user
    except HTTPException as e:
        raise e
    except Exception as e:
        print(e)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="更新用户角色失败，请稍后重试",
        )

@router.get("/roles", response_model=List[RoleResponse])
async def list_roles(
    skip: int = 0,
    limit: int = 100,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_active_user)
):
    """
    获取角色列表接口
    返回系统中的所有角色信息
    Args:
        skip (int): 跳过的记录数
        limit (int): 限制返回的记录数
        db (Session): 数据库会话
        current_user (User): 当前用户
    Returns:
        List[RoleResponse]: 角色列表
    """
    try:
        roles = db.query(Role).offset(skip).limit(limit).all()
        return roles
    except Exception as e:
        print(e)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="获取角色列表失败，请稍后重试",
        )

@router.post("/roles", response_model=RoleResponse)
async def create_role(
    role: RoleCreate,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_active_admin)
):
    """
    创建角色接口（仅管理员）
    创建新的角色
    Args:
        role (RoleCreate): 角色创建信息
        db (Session): 数据库会话
        current_user (User): 当前管理员用户
    Returns:
        RoleResponse: 创建的角色信息
    Raises:
        HTTPException: 当角色已存在时抛出异常
    """
    try:
        # 检查角色是否已存在
        db_role = db.query(Role).filter(Role.name == role.name).first()
        if db_role:
            raise HTTPException(status_code=400, detail="角色已存在")
        
        # 创建角色
        db_role = Role(**role.dict())
        db.add(db_role)
        db.commit()
        db.refresh(db_role)
        
        return db_role
    except HTTPException as e:
        raise e
    except Exception as e:
        print(e)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="创建角色失败，请稍后重试",
        )

@router.get("/permissions", response_model=List[PermissionResponse])
async def list_permissions(
    skip: int = 0,
    limit: int = 100,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_active_user)
):
    """
    获取权限列表接口
    返回系统中的所有权限信息
    Args:
        skip (int): 跳过的记录数
        limit (int): 限制返回的记录数
        db (Session): 数据库会话
        current_user (User): 当前用户
    Returns:
        List[PermissionResponse]: 权限列表
    """
    try:
        permissions = db.query(Permission).offset(skip).limit(limit).all()
        return permissions
    except Exception as e:
        print(e)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="获取权限列表失败，请稍后重试",
        )

@router.post("/permissions", response_model=PermissionResponse)
async def create_permission(
    permission: PermissionCreate,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_active_admin)
):
    """
    创建权限接口（仅管理员）
    创建新的权限
    Args:
        permission (PermissionCreate): 权限创建信息
        db (Session): 数据库会话
        current_user (User): 当前管理员用户
    Returns:
        PermissionResponse: 创建的权限信息
    Raises:
        HTTPException: 当权限已存在时抛出异常
    """
    try:
        # 检查权限是否已存在
        db_permission = db.query(Permission).filter(Permission.name == permission.name).first()
        if db_permission:
            raise HTTPException(status_code=400, detail="权限已存在")
        
        # 创建权限
        db_permission = Permission(**permission.dict())
        db.add(db_permission)
        db.commit()
        db.refresh(db_permission)
        
        return db_permission
    except HTTPException as e:
        raise e
    except Exception as e:
        print(e)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="创建权限失败，请稍后重试",
        )

@router.post("/role-permissions", response_model=dict)
async def assign_permission_to_role(
    assignment: RolePermissionAssign,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_active_admin)
):
    """
    为角色分配权限接口（仅管理员）
    将指定权限分配给指定角色
    Args:
        assignment (RolePermissionAssign): 角色权限分配信息
        db (Session): 数据库会话
        current_user (User): 当前管理员用户
    Returns:
        dict: 分配成功信息
    Raises:
        HTTPException: 当角色或权限不存在，或关联已存在时抛出异常
    """
    try:
        # 检查角色是否存在
        role = db.query(Role).filter(Role.id == assignment.role_id).first()
        if not role:
            raise HTTPException(status_code=400, detail="角色不存在")
        
        # 检查权限是否存在
        permission = db.query(Permission).filter(Permission.id == assignment.permission_id).first()
        if not permission:
            raise HTTPException(status_code=400, detail="权限不存在")
        
        # 检查关联是否已存在
        role_permission = db.query(RolePermission).filter(
            RolePermission.role_id == assignment.role_id,
            RolePermission.permission_id == assignment.permission_id
        ).first()
        
        if role_permission:
            raise HTTPException(status_code=400, detail="角色权限关联已存在")
        
        # 创建角色权限关联
        role_permission = RolePermission(
            role_id=assignment.role_id,
            permission_id=assignment.permission_id
        )
        db.add(role_permission)
        db.commit()
        
        return {"message": "权限分配成功"}
    except HTTPException as e:
        raise e
    except Exception as e:
        print(e)
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="分配权限失败，请稍后重试",
        )

def verify_password(plain_password, hashed_password):
    """
    验证密码
    比较明文密码和哈希密码是否匹配
    Args:
        plain_password (str): 明文密码
        hashed_password (str): 哈希密码
    Returns:
        bool: 密码是否匹配
    """
    return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password):
    """
    生成密码哈希
    将明文密码转换为哈希密码
    Args:
        password (str): 明文密码
    Returns:
        str: 哈希密码
    """
    return pwd_context.hash(password)

def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    """
    创建访问令牌
    生成JWT访问令牌
    Args:
        data (dict): 要编码的数据
        expires_delta (Optional[timedelta]): 过期时间增量
    Returns:
        str: JWT访问令牌
    """
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt